package com.example.myapplication.JavaParser;

import android.view.View;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.ArrayInitializerExpr;
import com.github.javaparser.ast.expr.FieldAccessExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.visitor.GenericVisitor;
import com.github.javaparser.ast.visitor.ModifierVisitor;
import com.github.javaparser.ast.visitor.Visitable;
import com.github.javaparser.ast.visitor.VoidVisitor;
import com.github.javaparser.printer.DefaultPrettyPrinter;
import com.github.javaparser.resolution.MethodUsage;
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.google.common.base.CaseFormat;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @description:
 * @author: wangrongfeng
 * @create: 2023-06-28 11:32
 **/
public class RemoveButterKnife {

    public static void main(String[] args) {
        //"C:\\Users\\Rong\\AndroidStudioProjects\\dfdj-android2\\dfdj-android\\live\\src\\main\\java\\com\\daofeng\\live"
        //"C:\\Users\\Rong\\AndroidStudioProjects\\dfdj-android2\\dfdj-android\\order\\src\\main\\java\\com\\daofeng\\order"
        //"C:\\Users\\Rong\\AndroidStudioProjects\\dfdj-android2\\dfdj-android\\baselibrary\\src\\main\\java\\com\\daofeng"
        //"C:\\Users\\Rong\\AndroidStudioProjects\\dfdj-android2\\dfdj-android\\im\\src\\main\\java\\com\\daofeng\\im"
        //"C:\\Users\\Rong\\AndroidStudioProjects\\dfdj-android2\\dfdj-android\\chatroom\\src\\main\\java\\com\\daofeng\\chatroom"
        //"C:\\Users\\Rong\\AndroidStudioProjects\\dfdj-android2\\dfdj-android\\app\\src\\main\\java\\com\\daofeng\\peiwan"
        File folder = new File("C:\\Users\\Rong\\AndroidStudioProjects\\dfdj-android2\\dfdj-android\\app\\src\\main\\java\\com\\daofeng\\peiwan\\mvp\\home\\ui");
        printDirByRecursive(folder,0);


    }
    public static void printDirByRecursive(File dir,int level) {
        //获取这个目录下所有的子文件和子目录的数组。
        File[] files = dir.listFiles();
        //遍历这个数组，取出每个File对象
        if (files != null) {
            for (File f : files) {
                //判断这个File是否是一个文件，是：
                if (f.isFile()) {
//                    if(f.getName().endsWith(".java")){
                        new Execute().start(f.getAbsolutePath());
//                    }
                } else {//否则就是一个目录，继续递归
                    //递归调用
                    printDirByRecursive(f,level+1);
                }
            }
        }
    }

    static class Execute {
        boolean isReWrite = false;

        public void start(String fileName ) {
            JavaParser javaParser = new JavaParser();
            File file = new File(fileName);
            try {
                ParseResult<CompilationUnit> parseResult = javaParser.parse(file);
                if(!parseResult.getResult().isPresent()){
                    return;
                }
                CompilationUnit compilationUnit = parseResult.getResult().get();

                if(!compilationUnit.getPackageDeclaration().isPresent()){
                    return;
                }

                String pack = compilationUnit.getPackageDeclaration().get().getNameAsString();
                String className = compilationUnit.getTypes().get(0).getNameAsString();

                if(!compilationUnit.getClassByName(className).isPresent()){
                    return;
                }

                ClassOrInterfaceDeclaration type = compilationUnit.getClassByName(className).get();

                MethodDeclaration methodDeclaration = new MethodDeclaration();
                methodDeclaration.setType(Void.TYPE);
                methodDeclaration.setName("findViews");
                methodDeclaration.setModifiers(Modifier.Keyword.PRIVATE);

                if (className.contains("Activity") || className.contains("Fragment")) {

                } else {
                    methodDeclaration.addAndGetParameter(View.class, "view");
                }


                BlockStmt blockStmt = methodDeclaration.createBody();
                methodDeclaration.setBody(blockStmt);

                type.addMember(methodDeclaration);

                compilationUnit.accept(new ModifierVisitor<Void>() {
                    @Override
                    public Node visit(final ImportDeclaration node, final Void arg) {
                        String name = node.getName().asString();
                        if (name.contains("butterknife") || name.contains("R2")) {
                            node.remove();
                            isReWrite = true;
                        }
                        return super.visit(node, arg);
                    }

                    @Override
                    public Visitable visit(MethodCallExpr node, Void arg) {
                        return super.visit(node, arg);
                    }

                    @Override
                    public Visitable visit(final MethodDeclaration node, final Void arg) {
                        if (isReWrite && node.toString().startsWith("@OnClick")) {
                            List<String> ids = new ArrayList<>();
                            SingleMemberAnnotationExpr singleMemberAnnotationExpr = (SingleMemberAnnotationExpr) node.getChildNodes().get(0);
                            if (singleMemberAnnotationExpr.getMemberValue() instanceof FieldAccessExpr) {
                                ids.add(singleMemberAnnotationExpr.getMemberValue().toString().replace("R2", "R"));

                            } else if (singleMemberAnnotationExpr.getMemberValue() instanceof ArrayInitializerExpr) {
                                for (Node item : singleMemberAnnotationExpr.getMemberValue().getChildNodes()) {
                                    ids.add(item.toString().replace("R2", "R"));
                                }
                            }
                            String methodName = node.getName().toString();
                            if (ids.size() > 0) {
                                boolean isMoreParameters = node.getParameters().size() > 0;
                                for (String item : ids) {
                                    NameExpr nameExpr;
                                    if (className.contains("Activity")) {
                                        nameExpr = new NameExpr("this");
                                    } else if (className.contains("Fragment")) {
                                        nameExpr = new NameExpr("contentView");
                                    } else {
                                        nameExpr = new NameExpr("view");
                                    }
                                    MethodCallExpr methodCallExpr = new MethodCallExpr(nameExpr, "findViewById");
                                    methodCallExpr.addArgument(item);
                                    NameExpr nameExpr2 = new NameExpr(methodCallExpr.toString());
                                    MethodCallExpr methodCallExpr2 = new MethodCallExpr(nameExpr2, "setOnClickListener");
                                    if (isMoreParameters) {
                                        methodCallExpr2.addArgument("this::" + methodName);
                                    } else {
                                        methodCallExpr2.addArgument("v -> " + methodName + "()");
                                    }
                                    blockStmt.addStatement(methodCallExpr2);
                                }
                                node.getChildNodes().get(0).remove();
                            }
                        }
                        boolean isActivity = (className.contains("Activity") || className.contains("Fragment")) && node.toString().contains("initData");
                        boolean isDialog = (className.contains("Dialog")) && node.toString().contains("convertView");
                        if (isActivity || isDialog){
                            if(!node.getBody().isPresent()){
                                return super.visit(node, arg);
                            }
                            BlockStmt blockStmt1 = node.getBody().get();
                            for (Node item: blockStmt1.getStatements()  ) {
                                if(item.toString().startsWith("ButterKnife")){
                                    item.remove();
                                    break;
                                }
                            }
                            NameExpr nameExpr = new NameExpr("this");
                            MethodCallExpr methodCallExpr = new MethodCallExpr(nameExpr, "findViews");
                            if(isDialog){
                                methodCallExpr.addArgument("holder.getConvertView()");
                            }
                            blockStmt1.addStatement(0, methodCallExpr);
                        }
                        return super.visit(node, arg);
                    }

                    @Override
                    public Visitable visit(final FieldDeclaration node, final Void arg) {
                        if (isReWrite && node.toString().contains("@BindView") && node.getAnnotations().size()>0) {
                            String variableName = node.getVariables().get(0).toString();
                            List<Node> nodeList =    node.getAnnotations().get(0).getChildNodes();
                            String resId =  nodeList.get(nodeList.size()-1).toString().replace("R2", "R");
                            NameExpr nameExpr;
                            if (className.contains("Activity")) {
                                nameExpr = new NameExpr(variableName + " = this");
                            } else if (className.contains("Fragment")) {
                                nameExpr = new NameExpr(variableName + " = contentView");
                            }else {
                                nameExpr = new NameExpr(variableName + " = view");
                            }

                            MethodCallExpr methodCallExpr = new MethodCallExpr(nameExpr, "findViewById");

                            methodCallExpr.addArgument(resId);

                            blockStmt.addStatement(methodCallExpr);

                            if(nodeList.size()>2){

                            }

                            for (Node item : node.getChildNodes()  ) {
                                if(item.toString().contains("BindView")){
                                    item.remove();
                                    break;
                                }
                            }
                        }
                        return super.visit(node, arg);
                    }

                }, null);

                if (isReWrite) {
                    String outputStr = new DefaultPrettyPrinter().print(parseResult.getResult().get());
                    FileWriter writer = new FileWriter(fileName);
                    writer.write(outputStr);
                    writer.close();
                    System.out.println(fileName);
                    System.out.println(pack + "." + className);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }


}
